package org.dormi.learn.utils;

import org.dormi.learn.primitive.Currency;
import org.dormi.learn.primitive.Money;

import java.math.BigDecimal;
import java.util.Objects;

/**
 * 钱计算
 * 钱 = 数量 + 币种, 计算时 考虑货币精度和 舍入模式(如四舍五入)
 *
 * @author : dormi330
 * @version : 1.0
 */
public final class MoneyUtils {

    private MoneyUtils() {
        // not allowed
    }

    public static Money Zero(Currency currency) {
        return new Money(BigDecimal.ZERO, currency);
    }

    /** 是否 > 0 */
    public static boolean isPositive(Money money) {
        return money.getAmount().compareTo(BigDecimal.ZERO) > 0
                && money.getCurrency() != null;
    }

    /** 精度, 保留到小数点后多少位 */
    public static final int SCALE_2 = 2;

    /** 精度, 保留到小数点后多少位 */
    public static final int SCALE_6 = 6;

    /** 精度模式 BigDecimal.ROUND_HALF_UP(4) = 四舍五入 */
    public static final int ROUND_HALF_UP = BigDecimal.ROUND_HALF_UP;

    /** 抹零方式时, 采用的计算模式 */
    public static final int ROUND_MODE_IGNORE_SMALL = BigDecimal.ROUND_FLOOR;

    /* === 加减乘除 === */
    /* === 加减乘除 === */
    /* === 加减乘除 === */

    private static void checkCurrencySame(Money m1, Money m2) {
        if (m1.getCurrency() == null || m2.getCurrency() == null) {
            throw new RuntimeException("必须设置货币单位, 才能进行运算");
        }
        if (!Objects.equals(m1.getCurrency(), m2.getCurrency())) {
            throw new RuntimeException("必须使用同样的货币单位, 才能进行运算");
        }
    }

    /* === 加加加加 === */
    /* === 加加加加 === */
    /* === 加加加加 === */

    /**
     * 金钱相加
     *
     * @param m1        钱1
     * @param m2        钱2
     * @param scale     小数位
     * @param roundMode round模式(4=四舍五入)
     * @return 结果钱数
     */
    public static Money add(Money m1, Money m2, int scale, int roundMode) {
        checkCurrencySame(m1, m2);

        BigDecimal result = m1.getAmount()
                .add(m2.getAmount())
                .setScale(scale, roundMode);
        return new Money(result, m1.getCurrency());
    }

    /**
     * 金钱相加, 2位小数, 四舍五入
     *
     * @param m1 钱1
     * @param m2 钱2
     */
    public static Money add(Money m1, Money m2) {
        return add(m1, m2, SCALE_2, BigDecimal.ROUND_HALF_UP);
    }

    /* === 减减减减 === */
    /* === 减减减减 === */
    /* === 减减减减 === */

    /**
     * 金钱相减 m1-m2
     *
     * @param m1        钱1
     * @param m2        钱2
     * @param scale     小数位
     * @param roundMode round模式(4=四舍五入)
     * @return 结果钱数
     */
    public static Money subtract(Money m1, Money m2, int scale, int roundMode) {
        BigDecimal result = m1.getAmount()
                .subtract(m2.getAmount())
                .setScale(scale, roundMode);
        return new Money(result, m1.getCurrency());
    }

    /**
     * 金钱相减 m1-m2 , 2位小数, 四舍五入
     *
     * @param m1 钱1
     * @param m2 钱2
     */
    public static Money subtract(Money m1, Money m2) {
        return subtract(m1, m2, SCALE_2, BigDecimal.ROUND_HALF_UP);
    }

    /* === 乘乘乘乘 === */
    /* === 乘乘乘乘 === */
    /* === 乘乘乘乘 === */

    /**
     * 金钱 x 一个数字
     *
     * @param m1        钱1
     * @param num       数字
     * @param scale     小数位(2=保留2位小数)
     * @param roundMode round模式(4=四舍五入)
     * @return 结果钱数
     */
    public static Money multiply(Money m1, BigDecimal num, int scale, int roundMode) {
        if (m1.getCurrency() == null) {
            throw new RuntimeException("必须设置货币单位, 才能进行运算");
        }

        BigDecimal result = m1.getAmount()
                .multiply(num)
                .setScale(scale, roundMode);
        return new Money(result, m1.getCurrency());
    }

    /**
     * 金钱 x 一个数字, 2位小数, 四舍五入
     *
     * @param m1  钱1
     * @param num 数字
     * @return 结果钱数
     */
    public static Money multiply(Money m1, BigDecimal num) {
        return multiply(m1, num, SCALE_2, BigDecimal.ROUND_HALF_UP);
    }

    /* === 除除除除 === */
    /* === 除除除除 === */
    /* === 除除除除 === */

    /**
     * @param m1        钱1
     * @param num       数字
     * @param scale     小数位(典型值 2, 6)
     * @param roundMode round模式(4=四舍五入, 3=抹零方式)
     * @return 除法结果
     */
    public static Money divide(Money m1, BigDecimal num, int scale, int roundMode) {

        if (m1.getCurrency() == null) {
            throw new RuntimeException("必须设置货币单位, 才能进行运算");
        }

        BigDecimal result = m1.getAmount()
                .divide(num, scale, roundMode);
        return new Money(result, m1.getCurrency());
    }

    /**
     * 金钱 / 一个数字, 2位小数, 四舍五入
     *
     * @param m1  钱1
     * @param num 数字
     * @return 结果钱数
     */
    public static Money divide(Money m1, BigDecimal num) {
        return divide(m1, num, SCALE_2, BigDecimal.ROUND_HALF_UP);
    }

}
